using System.Diagnostics;
using DimensionsHelper.LanguageService.Syntax;

namespace DimensionsHelper.LanguageService;

public abstract class AnalysisMetadataNode(
    AnalysisBoundKinds boundKind,
    Declaration syntaxNode,
    AnalysisNode? parent)
    : AnalysisDeclaration(boundKind, syntaxNode, parent)
{
    public override bool IsMetadata => true;

    public override Declaration Syntax { get; } = syntaxNode;
}


public abstract class AnalysisMetadataDeclarationWithChildren(
    AnalysisBoundKinds boundKinds,
    Declaration syntax,
    AnalysisNode? parent)
    : AnalysisMetadataNode(boundKinds, syntax, parent)
{
    protected readonly List<AnalysisNode> Children = [];
    public override Declaration Syntax { get; } = syntax;

    public override int ChildrenCount => Children.Count + 1;

    public override IEnumerable<AnalysisNode> GetChildren()
    {
        return [NameIdentifier, ..Children];
    }

    internal override void AddChild(AnalysisNode child)
    {
        Children.Add(child);
    }
}


public class AnalysisMetadataArealDeclaration(
    MetadataArealDeclaration syntaxNode,
    AnalysisNode? parent)
    : AnalysisMetadataNode(AnalysisBoundKinds.MetadataArealDeclaration, syntaxNode, parent)
{
    private readonly List<AnalysisString> _children = [];

    public override MetadataArealDeclaration Syntax { get; } = syntaxNode;

    public override int ChildrenCount => _children.Count;

    public override IEnumerable<AnalysisNode> GetChildren()
    {
        return _children;
    }

    internal override void AddChild(AnalysisNode child)
    {
        Debug.Assert(child.BoundKind == AnalysisBoundKinds.Literal);
        Debug.Assert(_children.Count < 3);
        _children.Add((AnalysisString)child);
    }
}


public class AnalysisMetadataProperty(
    MetadataProperty syntaxNode,
    AnalysisNode? parent)
    : AnalysisMetadataNode(AnalysisBoundKinds.MetadataProperty, syntaxNode, parent);


public class AnalysisMetadataTemplate(
    MetadataTemplate syntaxNode,
    AnalysisNode? parent)
    : AnalysisMetadataNode(AnalysisBoundKinds.MetadataTemplate, syntaxNode, parent);


public abstract class AnalysisMetadataListWithAreal<TAnalysis, TSyntax> : AnalysisNode
    where TAnalysis : AnalysisMetadataNode
    where TSyntax : SyntaxNode
{
    protected AnalysisMetadataListWithAreal(
        AnalysisBoundKinds boundKind,
        MetadataListWithAreal<TSyntax> syntaxNode,
        AnalysisNode? parent)
        : base(boundKind, syntaxNode, parent)
    {
        if (!Identifier.IsDefaultOrEmpty(syntaxNode.AreaName))
        {
            ArealNameIdentifier = AnalysisNodeFactory.Identifier(syntaxNode.AreaName, this, null);
        }

        Syntax = syntaxNode;
    }

    public override MetadataListWithAreal<TSyntax> Syntax { get; }

    public AnalysisIdentifier? ArealNameIdentifier { get; }

    protected List<TAnalysis> Children { get; } = [];


    public override bool IsExpression => false;

    public override bool IsStatement => true;

    public override bool IsMetadata => true;

    public override int ChildrenCount => Children.Count + (ArealNameIdentifier != null ? 1 : 0);

    public override IEnumerable<AnalysisNode> GetChildren()
    {
        if (ArealNameIdentifier != null)
        {
            return [ArealNameIdentifier, ..Children];
        }

        return Children;
    }

    internal override void AddChild(AnalysisNode child)
    {
        Debug.Assert(child is TAnalysis);
        Children.Add((TAnalysis)child);
    }
}


public class AnalysisMetadataProperties(MetadataProperties syntax, AnalysisNode? parent)
    : AnalysisMetadataListWithAreal<AnalysisMetadataProperty, MetadataProperty>(
        AnalysisBoundKinds.MetadataProperties, syntax, parent)
{
    public override MetadataProperties Syntax { get; } = syntax;
}


public class AnalysisMetadataTemplates(MetadataTemplates syntax, AnalysisNode? parent)
    : AnalysisMetadataListWithAreal<AnalysisMetadataTemplate, MetadataTemplate>(
        AnalysisBoundKinds.MetadataTemplates, syntax, parent)
{
    public override MetadataTemplates Syntax { get; } = syntax;
}


public class AnalysisMetadataStyleProperty(MetadataStyleProperty syntax, AnalysisNode? parent)
    : AnalysisMetadataDeclarationWithChildren(AnalysisBoundKinds.MetadataStyleProperty, syntax, parent)
{
    public override MetadataStyleProperty Syntax { get; } = syntax;
}


public class AnalysisMetadataStyle(MetadataStyle syntax, AnalysisNode? parent)
    : AnalysisMetadataListWithAreal<AnalysisMetadataStyleProperty, MetadataStyleProperty>(
        AnalysisBoundKinds.MetadataStyle, syntax, parent)
{
    public override MetadataStyle Syntax { get; } = syntax;
}


public class AnalysisMetadataSuffix(Declaration syntaxNode, AnalysisNode? parent)
    : AnalysisMetadataDeclarationWithChildren(AnalysisBoundKinds.MetadataSuffix, syntaxNode, parent);


public class AnalysisMetadataCategory(MetadataCategory syntaxNode, AnalysisNode? parent)
    : AnalysisMetadataDeclarationWithChildren(AnalysisBoundKinds.MetadataCategory, syntaxNode, parent)
{
    public override MetadataCategory Syntax { get; } = syntaxNode;
}


public class AnalysisMetadataListUsage(MetadataListReference syntaxNode, AnalysisNode? parent)
    : AnalysisMetadataNode(AnalysisBoundKinds.MetadataListUsage, syntaxNode, parent)
{
    public override MetadataListReference Syntax { get; } = syntaxNode;
}


public class AnalysisMetadataFieldReference(MetadataFieldReference syntaxNode, AnalysisNode? parent)
    : AnalysisMetadataNode(AnalysisBoundKinds.MetadataFieldReference, syntaxNode, parent)
{
    public override MetadataFieldReference Syntax { get; } = syntaxNode;
}


public class AnalysisMetadataField(MetadataField syntaxNode, AnalysisNode? parent)
    : AnalysisMetadataDeclarationWithChildren(AnalysisBoundKinds.MetadataField, syntaxNode, parent)
{
    public override MetadataField Syntax { get; } = syntaxNode;
}


public class AnalysisMetadataClass(MetadataClass syntaxNode, AnalysisNode? parent)
    : AnalysisMetadataDeclarationWithChildren(AnalysisBoundKinds.MetadataClass, syntaxNode, parent)
{
    public override MetadataClass Syntax { get; } = syntaxNode;
}